package org.sdnstproject.interlayer.utils;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Properties;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;

public class SSHShell implements Runnable{
	private static final Logger log = LoggerFactory.getLogger(SSHShell.class);
	/** 设置编码格式*/
	private String charset = "UTF-8"; 
	/**远程主机的ip地址*/
	public String ip;
    /**远程主机登录用户名*/
    public String user;
    /**远程主机的登录密码*/
    public String passwd;
    /**设置ssh连接的远程端口*/
    public int port = 22;  
    /**保存输出内容的容器*/
    public ArrayList<String> out;
    /**ssh连接会话*/
    private Session session;
    private JSch jsch;
    private String command;
 
	/**
	 * 初始化登录信息.
	 * @param user用户名
	 * @param passwd密码
	 * @param ip主机IP
	 */
	public SSHShell(String user, String ip, String passwd ,int port ) {
		this.user = user;
		this.passwd = passwd;
		this.ip = ip;
		this.port = port;
		out = new ArrayList<String>();
	}
	public SSHShell(String user, String ip, String passwd) {
		this.user = user;
		this.passwd = passwd;
		this.ip = ip;
		out = new ArrayList<String>();
	}
	public SSHShell(String user, String ip, String passwd, String command) {
		this.user = user;
		this.passwd = passwd;
		this.ip = ip;
		this.command = command;
		out = new ArrayList<String>();
	}
	
	/**
	 * 连接到指定的IP.
	 * @throws JSchException
	 */
	public void connect() throws JSchException {
		jsch = new JSch();
		session = jsch.getSession(user, ip, port);
		session.setPassword(passwd);
		Properties config = new Properties();
		/*”如果设置成“yes”，ssh就不会自动把计算机的密匙加入“$HOME/.ssh/known_hosts”文件，
		 * 并且一旦计算机的密匙发生了变化，就拒绝连接。
		 * */
		config.put("StrictHostKeyChecking", "no");
		session.setConfig(config);
		session.connect();
	}
	/**
	 * 关闭连接.
	 */
	public void disconnect() throws Exception{
		if(session != null && session.isConnected()){
			session.disconnect();
		}
	}
	/**
	 * 执行一条命令.
	 */
	public void execCmd(String command) throws Exception{
		BufferedReader reader = null;
		ChannelExec channel = null;
		// 打开一个通道（本质上是一个线程）
	    // 由于socket本身是非线程安全的，所以不能同时打开多个channel
	    // 且多个channel不能同时发送命令到服务器，这点需要非常注意
		channel = (ChannelExec) session.openChannel("exec");
		channel.setCommand(command);
		channel.setInputStream(null);
		/*channel.setOutputStream(System.out);
      	  错误信息输出流，用于输出错误的信息，当exitstatus<0的时候
      	  FileOutputStream fos=new FileOutputStream("/tmp/stderr");
          ((ChannelExec)channel).setErrStream(fos);
		 * */
		channel.setExtOutputStream(System.out);
		channel.setErrStream(System.err);
		channel.connect();
		InputStream in = channel.getInputStream();
		reader = new BufferedReader(new InputStreamReader(in,
				Charset.forName(charset)));
		String buf = null;
		while ((buf = reader.readLine()) != null) {
			out.add(buf);
		}
		channel.disconnect();
	}
	
	/**
	 * 远程shell.
	 */
	public void execCmd() {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		String command = "";
		BufferedReader reader = null;
		Channel channel = null;
 
		try {
			while ((command = br.readLine()) != null) {
				channel = session.openChannel("exec");
				((ChannelExec) channel).setCommand(command);
				channel.setInputStream(null);
				((ChannelExec) channel).setErrStream(System.err);
				
				channel.connect();
				InputStream in = channel.getInputStream();
				reader = new BufferedReader(new InputStreamReader(in,
						Charset.forName(charset)));
				String buf = null;
				while ((buf = reader.readLine()) != null) {
					System.out.println(buf);
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		} catch (JSchException e) {
			e.printStackTrace();
		} finally {
			try {
				reader.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			channel.disconnect();
		}
	}
	/**
	 * 上传文件.
	 * @param local 本地要上传的文件.
	 * @param remote 接收地址.
	 * 例：ssh.uploadFile("/home/delta/Downloads/run.sh","/home/ubuntu/test/run.sh",);
	 */
	public void uploadFile(String local,String remote) throws Exception {
		ChannelSftp channel = null;
		InputStream inputStream = null;
		try {
			channel = (ChannelSftp) session.openChannel("sftp");
			channel.connect(5000);
			inputStream = new FileInputStream(new File(local));
			channel.setInputStream(inputStream);
			channel.put(inputStream, remote);
		} catch (Exception e) {
			throw e;
		}finally{
			if(channel != null){
				channel.disconnect();
			}
			if(inputStream != null){
				inputStream.close();
			}
		}
	}
	/**
	 * 下载文件.
	 * @param remote 目标服务器上的文件.
	 * @param local 本地接收地址.
	 * 例:ssh.downloadFile("/home/ubuntu/test/run.sh","/home/delta/Downloads/run.sh");
	 */
	public void downloadFile(String remote,String local) throws Exception{
		ChannelSftp channel = null;
		OutputStream outputStream = null;
		try {
			channel = (ChannelSftp) session.openChannel("sftp");
			channel.connect(5000);
			outputStream = new FileOutputStream(new File(local));
			channel.get(remote, outputStream);
			outputStream.flush();
		} catch (Exception e) {
			throw e;
		}finally{
			if( channel != null){
				channel.disconnect();
			}
			if(outputStream != null){
				outputStream.close();
			}
		}
	}
	@Override
	public void run() {
		BufferedReader reader = null;
		ChannelExec channel = null;
		InputStream in = null;
		try {
			channel = (ChannelExec) session.openChannel("exec");
			channel.setCommand(command);
			channel.setInputStream(null);
			channel.setExtOutputStream(System.out);
			channel.setErrStream(System.err);
			channel.connect();
			in = channel.getInputStream();
			reader = new BufferedReader(new InputStreamReader(in,
					Charset.forName(charset)));
			String buf = null;
			while ((buf = reader.readLine()) != null) {
				out.add(buf);
//				System.out.println(buf);
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				reader.close();
				in.close();
				channel.disconnect();
				disconnect();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}